/*
Copyright (C) 2003-2004 Douglas Thain and the University of Wisconsin
Copyright (C) 2005- The University of Notre Dame
This software is distributed under the GNU General Public License.
See the file COPYING for details.
*/

#include "debug.h"
#include "domain_name_cache.h"
#include "macros.h"
#include "stringtools.h"
#include "full_io.h"
#include "xmalloc.h"
#include "full_io.h"

#include <stdlib.h>
#include <stdarg.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <time.h>
#ifdef HAS_ALLOCA_H
#include <alloca.h>
#endif

static int debug_fd = 2;
static char *debug_file = 0;
static int debug_file_size = 10485760;
static const char *program_name = "";
static INT64_T debug_flags = D_NOTICE;
static pid_t (*debug_getpid)() = getpid;

struct flag_info {
	const char *name;
	INT64_T flag;
};

static struct flag_info table[] = {
	{ "syscall",  D_SYSCALL },
	{ "notice",   D_NOTICE },
	{ "channel",  D_CHANNEL },
	{ "process",  D_PROCESS },
	{ "resolve",  D_RESOLVE },
	{ "libcall",  D_LIBCALL },
	{ "tcp",      D_TCP },
	{ "dns",      D_DNS },
	{ "auth",     D_AUTH },
	{ "local",    D_LOCAL },
	{ "http",     D_HTTP },
	{ "ftp",      D_FTP },
	{ "nest",     D_NEST },
	{ "chirp",    D_CHIRP },
	{ "landlord", D_LANDLORD },
	{ "multi",    D_MULTI },
	{ "dcap",     D_DCAP },
	{ "rfio",     D_RFIO },
	{ "glite",    D_GLITE },
	{ "lfc",      D_LFC },
	{ "gfal",     D_GFAL },
	{ "grow",     D_GROW },
	{ "pstree",   D_PSTREE },
	{ "alloc" ,   D_ALLOC },
	{ "cache",    D_CACHE },
	{ "poll",     D_POLL },
	{ "hdfs",     D_HDFS },
	{ "bxgrid",   D_BXGRID },
	{ "remote",   D_REMOTE },
	{ "debug",    D_DEBUG },
	{ "login",    D_LOGIN },
	{ "irods",    D_IRODS },
	{ "wq",       D_WQ },
	{ "all",      ~0 },
	{ "time",     0 },	/* backwards compatibility */
	{ "pid",     0 },	/* backwards compatibility */
	{ 0,0 }
};

struct fatal_callback {
	void (*callback) ();
	struct fatal_callback *next;
};

struct fatal_callback * fatal_callback_list = 0;


int  debug_flags_set( const char *flagname )
{
	struct flag_info *i;

	for(i=table;i->name;i++) {
		if(!strcmp(flagname,i->name)) {
			debug_flags |= i->flag;
			return 1;
		}
	}

	return 0;
}

void debug_flags_print( FILE *stream )
{
	int i;

	for(i=0;table[i].name;i++) {
		fprintf(stream,"%s ",table[i].name);
	}
}

static const char * flag_to_name( INT64_T flag )
{
	struct flag_info *i;

	for(i=table;i->name;i++) {
		if(i->flag&flag) return i->name;
	}

	return "debug";
}

static void do_debug( int is_fatal, INT64_T flags, const char *fmt, va_list args )
{
	char newfmt[65536];
	char buffer[65536];
	int length;

	struct timeval tv;
	struct tm *tm;

	gettimeofday(&tv,0);
	tm = localtime(&tv.tv_sec);

	sprintf(newfmt,"%04d/%02d/%02d %02d:%02d:%02d.%02ld [%d] %s: %s: %s",
	       tm->tm_year+1900,tm->tm_mon+1,tm->tm_mday,tm->tm_hour,tm->tm_min,tm->tm_sec,(long)tv.tv_usec/10000,(int)debug_getpid(),program_name,is_fatal ? "fatal " : flag_to_name(flags),fmt);

	vsprintf(buffer,newfmt,args);
	string_chomp(buffer);
	strcat(buffer,"\n");
	length = strlen(buffer);

	if(debug_file) {
		struct stat info;

		fstat(debug_fd,&info);
		if(info.st_size >= debug_file_size && debug_file_size!=0) {
			close(debug_fd);

			if(stat(debug_file,&info)==0) {
				if(info.st_size >= debug_file_size) {
					char *newname = alloca(strlen(debug_file)+5);
					sprintf(newname,"%s.old",debug_file);
					rename(debug_file,newname);
				}
			}

			debug_fd = open(debug_file,O_CREAT|O_TRUNC|O_WRONLY,0777);
			if(debug_fd<0) fatal("couldn't open %s: %s",debug_file,strerror(errno));
		}
	}

	full_write(debug_fd,buffer,length);
}

void debug( INT64_T flags, const char *fmt, ... )
{
	va_list args;
	va_start(args,fmt);

	if(flags&debug_flags) {
		int save_errno = errno;
		do_debug(0,flags,fmt,args);
		errno = save_errno;
	}

	va_end(args);
}

void fatal( const char *fmt, ... ) 
{
	struct fatal_callback *f;
	va_list args;
	va_start(args,fmt);

	do_debug(1,0,fmt,args);

	for(f=fatal_callback_list;f;f=f->next) {
		f->callback();
	}

	while(1) {
		kill(getpid(),SIGTERM);
		kill(getpid(),SIGKILL);
	}

	va_end(args);
}

void debug_config_fatal( void (*callback) () )
{
	struct fatal_callback *f;
	f = xxmalloc(sizeof(*f));
	f->callback = callback;
	f->next = fatal_callback_list;
	fatal_callback_list = f;
}

void debug_config( const char *name )
{
	const char *n = strdup(name);

	program_name = strrchr(n,'/');
	if(program_name) {
		program_name++;
	} else {
		program_name = n;
	}
}

void debug_config_file( const char *f )
{
	if(debug_file) {
		free(debug_file);
		debug_file = 0;
	}

	if(f) {
		debug_file = strdup(f);
		debug_fd = open(f,O_CREAT|O_APPEND|O_WRONLY,0777);
		if(debug_fd<0) fatal("couldn't open %s: %s",f,strerror(errno));		
	} else {
		debug_fd = 2;
	}
}

void debug_config_file_size( int size )
{
	debug_file_size = size;
}

void debug_config_getpid( pid_t (*getpidfunc)() )
{
	debug_getpid = getpidfunc;
}

int debug_flags_clear()
{
	INT64_T result = debug_flags;
	debug_flags = 0;
	return result;	
}

void debug_flags_restore( INT64_T fl )
{
	debug_flags = fl;
}

